package com.specter.mvc.web.spring;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ConventionController {

	@Autowired
	WebApplicationContext ctx;

	/**
	 * 获得所有Controller的配置情况，可以直接注入RequestHandlerMapping直接获取
	 */
	public void mapping(HttpServletRequest request, HttpServletResponse response) throws Exception {
		log.warn("Warning!!! This is a test! URL is {}", request.getRequestURL());
		// 请求url和处理方法的映射
		LinkedList<RequestToMethodItem> requestToMethodItemList = new LinkedList<>();
		// 获取所有的RequestMapping
		Map<String, HandlerMapping> alls = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, HandlerMapping.class, true, false);

		for (HandlerMapping handlerMapping : alls.values()) {
			// 本项目只需要RequestMappingHandlerMapping中的URL映射
			if (handlerMapping instanceof RequestMappingHandlerMapping requestMappingHandlerMapping) {
				Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
				for (Map.Entry<RequestMappingInfo, HandlerMethod> requestMappingInfoHandlerMethodEntry : handlerMethods.entrySet()) {
					RequestMappingInfo requestMappingInfo = requestMappingInfoHandlerMethodEntry.getKey();
					HandlerMethod mappingInfoValue = requestMappingInfoHandlerMethodEntry.getValue();

					RequestMethodsRequestCondition methodCondition = requestMappingInfo.getMethodsCondition();
					RequestMethod requestMethod = setFirst(methodCondition.getMethods());
					String requestType = requestMethod == null ? "GET/POST" : requestMethod.toString();

					// PatternsRequestCondition patternsCondition = requestMappingInfo.getPatternsCondition();
					PathPatternsRequestCondition patternsCondition = requestMappingInfo.getPathPatternsCondition();
					String url = setFirst(patternsCondition.getPatterns()).getPatternString();

					String controller = mappingInfoValue.getBeanType().getName();
					String method = mappingInfoValue.getMethod().getName();


					Class<?>[] methodParam = mappingInfoValue.getMethod().getParameterTypes();
					RequestToMethodItem item = new RequestToMethodItem(controller, method, methodParam, url, requestType);

					requestToMethodItemList.add(item);
				}
				break;
			}
		}
		requestToMethodItemList.sort(Comparator.comparing(RequestToMethodItem::getController).thenComparing(RequestToMethodItem::getUrl));
		response.setContentType("text/html;charset=utf-8");
		response.getWriter().println(toHTML(requestToMethodItemList));

	}

	@Data
	private class RequestToMethodItem {
		private String url;
		private String type;

		private String controller;
		private String method;
		private Class<?>[] parmas;

		public RequestToMethodItem(String controller, String method, Class<?>[] parmas, String url, String type) {
			super();
			this.controller = controller;
			this.method = method;
			this.parmas = parmas;
			this.url = url;
			this.type = type;
		}

	}

	private <T> T setFirst(java.util.Set<T> set) {
		if (set != null && !set.isEmpty()) {
			return set.stream().findFirst().orElse(null);
		} else {
			return null;
		}
	}

	private String toHTML(List<RequestToMethodItem> list) {
		String path = ctx.getServletContext().getContextPath();
		String head = """
				<html>
				<head>
				<title>SpringMVC Mapping</title>
				<style>
				table {border-collapse: collapse; width: 100%; box-shadow: 0 1px 3px rgba(0,0,0,0.2);}
				th, td {border: 1px solid #e0e0e0; padding: 12px 15px; text-align: left;}
				th {background-color: #f8f9fa; font-weight: 600; color: #2c3e50;}
				tr:nth-child(even) {background-color: #fafafa;}
				tr:hover {background-color: #f1f3f5;}
				a {color: #3498db; text-decoration: none;}
				a:hover {text-decoration: underline;}
				</style>
				</head>
				<body>
				<table border='1'>
				<tr style='font-weight:bold;'>
				<td>#</td><td>URL</td><td>REQUEST_TYPE</td><td>ACTION_CONTROLLER</td><td>ACTION_METHOD</td>
				</tr>
				""";
		StringBuilder html = new StringBuilder(head);
		for (int i = 0; i < list.size(); i++) {
			RequestToMethodItem it = list.get(i);
			html.append("<tr>");
			html.append("<td>" + (i + 1) + "</td>");
			html.append("<td><a href='" + path + it.url + "' target='_blank'>" + it.url + "</a></td>");
			html.append("<td>" + it.type + "</td>");
			html.append("<td>" + it.controller + "</td>");
			html.append("<td>" + it.method + "(" + a2s(it.parmas) + ")</td>");
			html.append("</tr>");
		}
		html.append("</table></body></html>");
		return html.toString();
	}

	private String a2s(Class<?>[] objs) {
		if (objs.length < 1) {
			return "";
		}

		StringBuilder str = new StringBuilder();
		for (Class<?> clazz : objs) {
			str.append(clazz.getSimpleName() + ",");
		}
		return str.substring(0, str.length() - 1);
	}
}
